package com.myk.game.gobangchess.rooms;

import com.myk.game.gobangchess.Room;
import com.myk.game.gobangchess.constants.ColorCost;
import com.myk.game.gobangchess.constants.WinMode;
import com.myk.game.gobangchess.signs.AroundSign;
import com.myk.game.gobangchess.signs.ColorSign;
import com.myk.game.gobangchess.signs.GroundSign;
import com.myk.game.gobangchess.signs.LineSign;
import com.myk.game.gobangchess.utils.GridsUtils;

import java.util.ArrayList;
import java.util.List;

/**
 * 璃棋
 */
public class LeaveRoom extends Room {

    private int[] lastPoint; //上一步落子的位置  row，col
    private int[] curPoint; //该回合落子位置  row，col
    private int[] choosePoint; //当前选中要移动的棋子坐标  row，col
    private List<int[]> forcePiecesList; //要被强制移动的棋子坐标集合
    private List<Integer> forceLinePosList; //不能留子的区域的坐标集合
    private final List<int[]> moveArrayList = new ArrayList<>(); // 棋子移动标记

    //江柯模式
    private static final String mode1 = "保守";
    //夏日模式
    private static final String mode2 = "激进";
    //日出模式
    private static final String mode3 = "均衡";

    public LeaveRoom(String roomName) {
        super(roomName, "夏日对小紫迫移棋的改版", "璃棋", GridsUtils.createEmptyGrids(10,10), true, true,mode1,mode2,mode3);
    }

    @Override
    public String getRuleText() {
        /*if (isCurMode(mode1)){
            return "10路棋盘，轮流落1枚己方棋子，\n" +
                    "若己方落子与对方上一手落子同线(直斜都算)，两子之间的格子形成一条禁区，\n" +
                    "落子方要将禁区内的棋子依次选中移动到禁区外，\n" +
                    "移动遵循如下规则(保守模式)：\n" +
                    "        1、如果选中的棋子周围一圈有空格(禁区除外)，那么只能移动到周围一圈的格子。\n"+
                    "        2、如果选中的棋子周围一圈没有空格，那么可以选择某个方向(禁区方向除外)的第一个空格落子。\n" +
                    "        3、如果所有方向都没有空格了，那么选中的棋子直接移除。\n" +
                    "己方棋子形成五子连珠获胜。";
        }
        else if (isCurMode(mode2)){
            return "10路棋盘，轮流落1枚己方棋子，\n" +
                    "若己方落子与对方上一手落子同线(直斜都算)，两子之间的格子形成一条禁区，\n" +
                    "落子方要将禁区内的棋子依次选中移动到禁区外，\n" +
                    "移动遵循如下规则(激进模式)：\n" +
                    "        1、选中的棋子可以选择某个方向(禁区方向除外)的第一个空格落子。\n" +
                    "        2、如果所有方向都没有空格了，那么选中的棋子直接移除。\n" +
                    "己方棋子形成五子连珠获胜。";
        }
        else {
            return "10路棋盘，轮流落1枚己方棋子，\n" +
                    "若己方落子与对方上一手落子同线(直斜都算)，两子之间的格子形成一条禁区，\n" +
                    "落子方要将禁区内的棋子依次选中移动到禁区外，\n" +
                    "移动遵循如下规则(均衡模式)：\n" +
                    "    对于己方的棋子：\n" +
                    "        1、选中的棋子可以选择某个方向(禁区方向除外)的第一个空格落子。\n" +
                    "        2、如果所有方向都没有空格了，那么选中的棋子直接移除。\n" +
                    "    对于对方的棋子：\n" +
                    "        1、如果选中的棋子周围一圈有空格(禁区除外)，那么选择周围一圈的空格移动。\n"+
                    "        2、如果周围一圈没有空格，那么选中的棋子直接移除。\n" +
                    "己方棋子形成五子连珠获胜。";
        }*/

        if (isCurMode(mode1)){
            return "【保守模式】10路棋盘，轮流落1枚己方棋子，\n" +
                    "若己方落子与对方上一手落子同线，两子之间将形成【禁区】。\n" +
                    "落子方要将【禁区】中的所有棋子「撤离」；\n" +
                    "        1、朝任意方向第1格「撤离」。\n"+
                    "        2、无法按照以上方式「撤离」时，可越子「撤离」。\n" +
                    "        3、无法按照以上方式「撤离」时，移除该棋子。\n" +
                    "获胜条件：己方棋子连五";
        }
        else if (isCurMode(mode2)){
            return "【激进模式】10路棋盘，轮流落1枚己方棋子，\n" +
                    "若己方落子与对方上一手落子同线，两子之间将形成【禁区】。\n" +
                    "落子方要将【禁区】中的所有棋子「撤离」；\n" +
                    "        1、朝任意方向的第1个空格「撤离」。\n" +
                    "        2、无法按照以上方式「撤离」时，移除该棋子。\n" +
                    "获胜条件：己方棋子连五";
        }
        else {
            return "【标准模式】10路棋盘，轮流落1枚己方棋子，\n" +
                    "若己方落子与对方上一手落子同线，两子之间将形成【禁区】。\n" +
                    "落子方要将【禁区】中的所有棋子「撤离」；\n" +
                    "        1、朝任意方向第1格「撤离」（己方棋子「撤离」时可越子）。\n" +
                    "        2、无法按照以上方式「撤离」时，移除该棋子。\n" +
                    "获胜条件：己方棋子连五";
        }
    }

    @Override
    public void resetGrids() {
        super.resetGrids();
        lastPoint = null;
        curPoint = null;
        choosePoint = null;
        forcePiecesList = null;
        forceLinePosList = null;
        moveArrayList.clear();
    }

    @Override
    public String getGameInfo() {
        //先清空棋盘
        signList.clear();
        //画上次落子框
        if (lastPoint != null){
            signList.add(new AroundSign(getIndex(lastPoint[0],lastPoint[1]),"#00000000","#6600FF00",1.2f));
        }
        //画当前落子框
        if (curPoint != null){
            signList.add(new AroundSign(getIndex(curPoint[0],curPoint[1]),"#00000000","#6600FF00",1.2f));
            //画禁止落子线
            if (forceLinePosList != null && forceLinePosList.size() > 0){
                signList.add(new ColorSign(forceLinePosList, "#66333333"));
            }
        }
        //画棋子选中框
        if (choosePoint != null){
            signList.add(new GroundSign(getIndex(choosePoint[0], choosePoint[1]), "#00FFFFFF", "#FF33CC33", 2F, 0, 1));
            List<Integer> aroundRange = getCanMoveRange(choosePoint[0], choosePoint[1]);
            if (aroundRange.size() > 0){
                signList.add(new ColorSign(aroundRange, "#6633CC33"));
            }
        }
        //画移动箭头
        for (int[] moveArray : moveArrayList) {
            signList.add(new LineSign(moveArray[0], moveArray[1], ColorCost.MOVE_ARROW_LIGHT, 1));
        }
        return super.getGameInfo();
    }

    @Override
    public void doRule(int row, int col, int value) {
        //当前有要被迫移的棋子时
        if (forcePiecesList !=null && forcePiecesList.size()>0){
            //当前有选中的棋子时
            if (choosePoint != null){
                //选中了可以的落点
                if (getCanMoveRange(choosePoint[0], choosePoint[1]).contains(getIndex(row,col))){
                    //添加移动箭头
                    moveArrayList.add(new int[]{getIndex(choosePoint[0], choosePoint[1]),getIndex(row,col)});
                    //更新棋子坐标
                    curGrids[row][col] = curGrids[choosePoint[0]][choosePoint[1]];
                    curGrids[choosePoint[0]][choosePoint[1]] = 0;
                    //更新迫移状态
                    forcePiecesList.removeIf(point -> point[0] == choosePoint[0] && point[1] == choosePoint[1]);
                    choosePoint = null;
                    //判断结束否？
                    if (!checkOver(value)){
                        //还有棋子没有移完
                        if (forcePiecesList.size()>0){
                            noticeAllRefreshGameInfo(); //刷新界面继续操作
                        }
                        //棋子全部迫移完
                        else {
                            lastPoint = curPoint;
                            curPoint = null;
                            changeMover(value);
                        }
                    }
                }
                //选中了新的迫移棋子时
                else if (isChooseForce(row, col)){
                    choosePoint = new int[]{row,col}; //选中要移动的棋子
                    noticeAllRefreshGameInfo();
                }
            }
            //没有选中的棋子时
            else {
                if (isChooseForce(row, col)){
                    choosePoint = new int[]{row,col}; //选中要移动的棋子
                    noticeAllRefreshGameInfo();
                }
            }
        }
        //没有迫移棋子，正常落子时
        else {
            if (curGrids[row][col] != 0) return; //只能落子在空格
            curGrids[row][col] = value;
            curPoint = new int[]{row,col};
            moveArrayList.clear();
            //形成连珠？
            if (checkOver(value)){
                return;
            }
            //检查是否夹住了棋子
            if (checkSideKing(row,col)) {
                return;
            }
            lastPoint = new int[]{row,col};
            curPoint = null;
            changeMover(value);
        }
    }

    //切换行动方
    private void changeMover(int value) {
        if (!checkDraw()){ //检查是否平局
            if (value == 1) {
                stepAdd();
                noticeWhiteMove();
            } else if (value == 2) {
                stepAdd();
                noticeBlackMove();
            }
        }
    }

    //检查是否平局
    private boolean checkDraw() {
        if (GridsUtils.getDataCount(curGrids, 0) == 0) {
            doOver(WinMode.BOTH_DRAW, "棋盘下满，没有形成连珠，和棋");
            return true;
        }
        return false;
    }

    //检查是否获胜
    private boolean checkOver(int value){
        if (checkFormation(value, 5) > 0){
            if (value == 1){
                stepAdd();
                doOver(WinMode.BLACK_WIN, "黑方形成五子连珠获胜");
            }
            else {
                stepAdd();
                doOver(WinMode.WHITE_WIN, "白方形成五子连珠获胜");
            }
            return true;
        }
        else {
            return false;
        }
    }

    // 判断位置在棋盘上
    protected boolean isInGrids(int row, int col) {
        return row >= 0 && row <= curGrids.length - 1 && col >= 0 && col <= curGrids[0].length - 1;
    }

    //判断指定落点和上一步落点的坐标是否直线斜线共线
    private boolean isSameLine(int row, int col) {
        if (lastPoint != null) {
            return lastPoint[0] == row || lastPoint[1] == col || Math.abs(row - lastPoint[0]) == Math.abs(col - lastPoint[1]);
        }
        return false;
    }

    //检查是否夹住了棋子
    private boolean checkSideKing(int row, int col) {
        if (isSameLine(row,col)){
            updPointsInLine(row,col); //更新强制移动的状态信息
            boolean needMove = this.forcePiecesList.size() > 0;
            if (needMove){
                noticeAllRefreshGameInfo(); //需要强制移动就更新一下界面，然后继续操作
            }
            return needMove;
        }
        return false;
    }

    //获取指定落点和上一个落点之间的棋子集合
    private void updPointsInLine(int row, int col) {
        this.forcePiecesList = new ArrayList<>();
        this.forceLinePosList = new ArrayList<>();
        int rowAdd = Integer.compare(row, lastPoint[0]);
        int colAdd = Integer.compare(col, lastPoint[1]);
        for (int i = (lastPoint[0] + rowAdd),j = (lastPoint[1] + colAdd); i != row || j != col; i += rowAdd, j += colAdd){
            if (curGrids[i][j] != 0){
                this.forcePiecesList.add(new int[]{i,j});
            }
            forceLinePosList.add(getIndex(i, j));
        }
    }

    //检查是否形成连珠
    private int checkFormation(int value, int winLength) {
        int rows = curGrids.length;
        int cols = curGrids[0].length;
        int formationCount = 0;

        // 水平方向
        for (int i = 0; i < rows; i++) {
            for (int j = 0; j < cols - winLength + 1; j++) {
                int length = 0;
                for (int k = j; k < j + winLength; k++) {
                    if (curGrids[i][k] == value) {
                        length++;
                    }
                    else {
                        break;
                    }
                }
                if (length >= winLength) {
                    formationCount += (length - winLength + 1);
                }
            }
        }

        // 垂直方向
        for (int i = 0; i < rows - winLength + 1; i++) {
            for (int j = 0; j < cols; j++) {
                int length = 0;
                for (int k = i; k < i + winLength; k++) {
                    if (curGrids[k][j] == value) {
                        length++;
                    }
                    else {
                        break;
                    }
                }
                if (length >= winLength) {
                    formationCount += (length - winLength + 1);
                }
            }
        }

        // 左上到右下方向
        for (int i = 0; i < rows - winLength + 1; i++) {
            for (int j = 0; j < cols - winLength + 1; j++) {
                int length = 0;
                for (int k = 0; k < winLength; k++) {
                    if (curGrids[i + k][j + k] == value) {
                        length++;
                    }
                    else {
                        break;
                    }
                }
                if (length >= winLength) {
                    formationCount += (length - winLength + 1);
                }
            }
        }

        // 右上到左下方向
        for (int i = 0; i < rows - winLength + 1; i++) {
            for (int j = winLength - 1; j < cols; j++) {
                int length = 0;
                for (int k = 0; k < winLength; k++) {
                    if (curGrids[i + k][j - k] == value) {
                        length++;
                    }
                    else {
                        break;
                    }
                }
                if (length >= winLength) {
                    formationCount += (length - winLength + 1);
                }
            }
        }

        return formationCount;
    }

    //该位置在被迫移棋子列表中
    private boolean isChooseForce(int row, int col) {
        for (int[] ints : forcePiecesList) {
            if (ints[0] == row && ints[1] == col) return true;
        }
        return false;
    }

    //获取可以移动坐标集合
    public List<Integer> getCanMoveRange(int row, int col){
        //保守模式
        if (isCurMode(mode1)){
            return getCanMoveConservativeRange(row, col);
        }
        //激进模式
        else if (isCurMode(mode2)){
            return getCanMoveRadicalRange(row, col);
        }
        //均衡模式
        else {
            if (curGrids[row][col] == cur){
                //对于己方棋子采用激进模式
                return getCanMoveRadicalRange(row, col);
            }
            else {
                //对于对方棋子采用极其保守模式
                return getCanMoveExtremelyConservativeRange(row, col);
            }
        }
    }

    //获取可以移动坐标集合 激进模式 radical
    private List<Integer> getCanMoveRadicalRange(int row, int col) {
        //获取当前落子位置往其它方向搜寻到的第一个空格的坐标集合
        List<Integer> rangeIndex = getSearchRange(row, col);
        //如果选中的棋子无路可走，那么就返回当前棋子坐标，玩家再次点击自己就清除掉自己
        if (rangeIndex.size() == 0){
            rangeIndex.add(getIndex(row, col));
        }
        return rangeIndex;
    }

    //获取可以移动坐标集合 保守模式 conservative
    private List<Integer> getCanMoveConservativeRange(int row, int col) {
        //获取周围一圈的空格
        List<Integer> rangeIndex = getAroundRange(row, col);
        //周围一圈没有空格就任选方向搜索第一个空格
        if (rangeIndex.size() == 0){
            rangeIndex = getSearchRange(row, col);
        }
        //还没有就返回自身坐标
        if (rangeIndex.size() == 0){
            rangeIndex.add(getIndex(row, col));
        }
        return rangeIndex;
    }

    //获取可以移动坐标集合 极其保守模式 extremely conservative
    private List<Integer> getCanMoveExtremelyConservativeRange(int row, int col) {
        //获取周围一圈的空格
        List<Integer> rangeIndex = getAroundRange(row, col);
        //周围一圈没有空格就返回自身坐标
        if (rangeIndex.size() == 0){
            rangeIndex.add(getIndex(row, col));
        }
        return rangeIndex;
    }

    //获取当前落子位置周围的空格坐标集合
    public List<Integer> getAroundRange(int row, int col){
        int[][] dxdys = {{-1,-1}, {-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
        List<Integer> rangeIndex = new ArrayList<>();
        for (int[] dxdy : dxdys) {
            int x = row + dxdy[0];
            int y = col + dxdy[1];
            if (x >= 0 && x < curGrids.length && y >= 0 && y < curGrids[0].length && curGrids[x][y] == 0){
                if (forceLinePosList != null && forceLinePosList.contains(getIndex(x, y))) continue;
                rangeIndex.add(getIndex(x, y));
            }
        }
        return rangeIndex;
    }

    //获取当前落子位置往其它方向搜寻到的第一个空格的坐标集合
    public List<Integer> getSearchRange(int row, int col){
        int[][] dxdys = {{-1,-1}, {-1,0},{-1,1},{0,-1},{0,1},{1,-1},{1,0},{1,1}};
        List<Integer> rangeIndex = new ArrayList<>();
        for (int[] dxdy : dxdys) {
            for (int x = row + dxdy[0], y = col + dxdy[1], i = 0; x >= 0 && x < curGrids.length && y >= 0 && y < curGrids[0].length; x += dxdy[0], y += dxdy[1], i++) {
                if (isOnForceLine(x, y)){
                    break;
                }
                if (curGrids[x][y] == 0) {
                    rangeIndex.add(getIndex(x, y));
                    break;
                }
            }
        }
        return rangeIndex;
    }

    //判断是否在禁区线上
    private boolean isOnForceLine(int x, int y) {
        if (lastPoint[1] == curPoint[1] && lastPoint[1] == y){
            return true;
        }
        float d1 = (float) (x - lastPoint[0]) / (y - lastPoint[1]);
        float d2 = (float) (x - curPoint[0]) / (y - curPoint[1]);
        return d1 == d2;
    }

    //剩余的棋子都不能移动了
    private boolean allCantMoveForceKing() {
        for (int[] ints : this.forcePiecesList) {
            if (getCanMoveRange(ints[0],ints[1]).size() > 0) return false;
        }
        return true;
    }

}
